package com.ruoyi.common.utils.modbus;

import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.dto.ModbusBath;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * modbus通讯工具类,采用modbus4j实现
 *
 * @author lsw
 * @dependencies modbus4j-3.0.3.jar
 * @website <a href="https://github.com/infiniteautomation/modbus4j">...</a>
 */
public class Modbus4jUtils {
    private static final Logger log = LoggerFactory.getLogger(Modbus4jUtils.class);

    // 获取连接管理器实例
    private static final ModbusConnectionManager connectionManager = ModbusConnectionManager.getInstance();

    /**
     * 获取master
     *
     * @return ModbusMaster
     * @throws ModbusInitException 异常
     */
    public static ModbusMaster getMaster(String host, int port) throws ModbusInitException {
        return connectionManager.getConnection(host, port);
    }

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param offset  位置
     * @return 读取值
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Boolean readCoilStatus(String host, int port, int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        return getMaster(host, port).getValue(loc);
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId 从站地址
     * @param offset  寄存器地址
     * @return Boolean
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Boolean readInputStatus(String host, int port, int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        return getMaster(host, port).getValue(loc);
    }


    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId  slave Id
     * @param offset   位置
     * @param dataType 数据类型,浮点双精度读取 DataType.FOUR_BYTE_FLOAT 单精度 DataType.TWO_BYTE_INT_SIGNED
     * @return Number
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Number readHoldingRegister(String host, int port, int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        return getMaster(host, port).getValue(loc);
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId  slaveId
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Number readInputRegisters(String host, int port, int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        return getMaster(host, port).getValue(loc);
    }

    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static void batchRead(String host, int port) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        BatchRead<Integer> batch = new BatchRead<>();

        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
        batch.addLocator(1, BaseLocator.inputStatus(1, 0));

        ModbusMaster master = getMaster(host, port);

        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        System.out.println(results.getValue(0));
        System.out.println(results.getValue(1));
    }

    public synchronized static Map<String, BatchResults<Integer>> batchRead(List<ModbusBath> modbusBaths) {
        log.info("【发送读取命令】");
        long start = System.currentTimeMillis();
        Map<String, BatchResults<Integer>> map = new HashMap<>();

        for (ModbusBath modBus : modbusBaths) {
            log.info("【发送读取命令】" + modBus);
            log.info("【发送读取命令】" + modBus.getHost() + modBus.getPort());
            log.info("【发送读取命令】" + modBus.getBatch());
            BatchResults<Integer> results = new BatchResults<>();
            try {
                ModbusMaster master = getMaster(modBus.getHost(), modBus.getPort());
                results = master.send(modBus.getBatch());
            } catch (Exception e) {
                log.warn(e.toString());
            }
            map.put(modBus.getHost() + modBus.getPort(), results);
            System.out.println(results);
            System.out.println(results.getValue(0));
            // 更新缓存
            SpringUtils.getBean(RedisCache.class).setCacheObject(modBus.getHost() + modBus.getPort(), results);
        }
        log.info("【发送读取命令】结果：" + map);
        log.info("【发送读取命令】耗时：" + (System.currentTimeMillis() - start));

        return map;
    }

    /**
     * 手动释放连接
     */
    public static void releaseConnection(String host, int port) {
        connectionManager.releaseConnection(host, port);
    }

    /**
     * 关闭指定连接
     */
    public static void closeConnection(String host, int port) {
        connectionManager.closeConnection(host, port);
    }

    /**
     * 关闭所有连接
     */
    public static void closeAllConnections() {
        connectionManager.forceCloseAllConnections();
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        String host = "192.168.1.10";
        int port = 502;
        int slaveId = 1;
        int offset = 68;
        try {
            while (offset < 75) {
                // 03测试
                Number v031 = readHoldingRegister(host, port, slaveId, offset, DataType.FOUR_BYTE_FLOAT);
                System.out.println("v031:" + v031);

                Number v032 = readHoldingRegister(host, port, slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);
                System.out.println("v032:" + v032);

                Number v033 = readHoldingRegister(host, port, slaveId, offset, DataType.TWO_BYTE_INT_SIGNED);
                System.out.println("v033:" + v033);

                offset++;

                // 测试连接超时功能，休眠35秒
                if (offset == 70) {
                    System.out.println("等待连接超时...");
                    Thread.sleep(35000);
                }
            }

            // 测试完成后手动释放连接
            releaseConnection(host, port);
        } catch (Exception e) {
            log.error("测试过程中发生错误", e);
        }
    }
}
